// Created by amoylel on 11/25/2018.
// Copyright (c) 2018 amoylel All rights reserved.

#ifndef AMO_BASIC_JSONDB_POOL_E1F91BF9_DB57_4B2E_B64A_F274910C5010_HPP__
#define AMO_BASIC_JSONDB_POOL_E1F91BF9_DB57_4B2E_B64A_F274910C5010_HPP__

#include<string>
#include <unordered_map>
#include <amo/impl/basic_path.hpp>
#include <amo/impl/basic_json.hpp>
#include <amo/looper_executor_pool.hpp>
#include <amo/thread.hpp>

namespace amo {


    template<typename CodingType>
    class basic_jsondb_pool {
    public:
        typedef basic_json<CodingType> json_type;
        typedef basic_path<CodingType> path_type;
        typedef basic_directory<CodingType> directory_type;
        typedef basic_json_object<CodingType> json_object_type;
    public:
        basic_jsondb_pool() {
            m_pool.reset(new amo::looper_executor_pool(6));
        }
        
        std::shared_ptr<json_type> get_json(const path_type& filepath) {
            auto p = filepath;
            p.normalize();
            std::string str = p.generic_string();
            
            {
                std::unique_lock<std::recursive_mutex> lock(m_mutex);
                auto iter = m_caches.find(str);
                
                if (iter != m_caches.end()) {
                    return iter->second;
                }
            }
            
            
            if (filepath.exists() && filepath.is_file()) {
                std::shared_ptr<json_type> json(new json_type());
                json->parse(str);
                
                if (json->is_valid()) {
                    {
                        std::unique_lock<std::recursive_mutex> lock(m_mutex);
                        m_caches.insert({ str, json });
                    }
                    
                    get_next_part(filepath);
                    m_pool->wait_for_empty();
                    return json;
                }
                
            }
            
            if (!filepath.exists()) {
                std::shared_ptr<json_type> json(new json_type(false));
                json->to_file(filepath.generic_string());
                {
                    std::unique_lock<std::recursive_mutex> lock(m_mutex);
                    m_caches.insert({ str, json });
                }
                return json;
            }
            
            return{};
            
        }
        
        void set_json(const path_type& path, const std::shared_ptr<json_type> ptr) {
            std::unique_lock<std::recursive_mutex> lock(m_mutex);
            
            if (!ptr || !ptr->is_valid()) {
                return;
            }
            
            auto p = path;
            p.normalize();
            std::string str = p.generic_string();
            m_caches.insert({str, ptr});
        }
        
        
        void need_update(const path_type& path, const std::shared_ptr<json_type> ptr) {
            ptr->to_file(path.generic_string());
        }
        
        void get_next_part(const path_type& filepath) {
            std::string ext = filepath.extension().string();
            
            if (ext.empty()) {
                return;
            }
            
            if (ext[0] == '.') {
                ext[0] = '0';
            }
            
            int index = amo::lexical_cast<int>(ext);
            ++index;
            path_type next_path = filepath;
            next_path.change_extension(std::string(".") + std::to_string(index));
            
            if (!next_path.exists()) {
                return;
            } else {
                get_next_part(next_path);
            }
            
            m_pool->push(std::bind(&basic_jsondb_pool::get_table_part, this, next_path));
            
            
            
        }
        
        void get_table_part(const path_type& filepath) {
            do {
                //$cdevel("read file[{}]: {}", amo::current_thread_id(), filepath.string());
                
                std::shared_ptr<json_type> json_ptr;
                json_ptr.reset(new json_type());
                json_ptr->parse(filepath.generic_string());
                
                if (!json_ptr || !json_ptr->is_valid()) {
                    break;
                }
                
                auto p = filepath;
                p.normalize();
                std::string str = p.generic_string();
                std::unique_lock<std::recursive_mutex> lock(m_mutex);
                m_caches.insert({ str, json_ptr });
            } while (false);
            
            
        }
        
    private:
        std::unordered_map <std::string, std::shared_ptr<json_type> > m_caches;
        std::shared_ptr<amo::looper_executor_pool> m_pool;
        std::recursive_mutex m_mutex;
    };
}

#endif // AMO_BASIC_JSONDB_POOL_E1F91BF9_DB57_4B2E_B64A_F274910C5010_HPP__

